package com.boby.homecamera.helper

import android.media.AudioAttributes
import android.media.AudioFormat
import android.media.AudioTrack
import android.media.MediaCodec
import android.media.MediaCodecInfo
import android.media.MediaFormat
import android.util.Log
import com.alexvas.rtsp.codec.AudioDecodeThread
import com.alexvas.rtsp.codec.AudioDecodeThread.Companion.getAacDecoderConfigData
import com.alexvas.rtsp.codec.FrameQueue
import com.boby.lib_common.BaseApplication
import com.boby.lib_common.util.BLog
import org.java_websocket.WebSocket
import java.io.File
import java.nio.ByteBuffer
import java.util.concurrent.LinkedBlockingQueue


class AudioPlayHelper(val webSocket: WebSocket?) {
    companion object{
        private  val TAG = AudioPlayHelper::class.simpleName
    }

        private var codec: MediaCodec? = null
        private var audioTrack: AudioTrack? = null
        private var running = false
    private var decodeThread : Thread? = null
    private var pcmThread : Thread? = null
    private var audioDataBytesQueue = LinkedBlockingQueue<ByteArray>(30)
    private var pcmDataBytesQueue = LinkedBlockingQueue<ByteArray>(30)
//    private var testFile : File? = null
        fun start() {
            val sampleRate = 16000
//            val minBuf = AudioTrack.getMinBufferSize(
//                sampleRate,
//                AudioFormat.CHANNEL_OUT_MONO,
//                AudioFormat.ENCODING_PCM_16BIT
//            )
//            audioTrack = AudioTrack.Builder()
//                .setAudioAttributes(
//                    AudioAttributes.Builder()
//                        .setUsage(AudioAttributes.USAGE_MEDIA)
//                        .setContentType(AudioAttributes.CONTENT_TYPE_MUSIC)
//                        .build()
//                )
//                .setAudioFormat(
//                    AudioFormat.Builder()
//                        .setEncoding(AudioFormat.ENCODING_PCM_16BIT)
//                        .setSampleRate(sampleRate)
//                        .setChannelMask(AudioFormat.CHANNEL_OUT_MONO)
//                        .build()
//                )
//                .setBufferSizeInBytes(minBuf)
//                .build()
//
//            audioTrack?.play()

            val outChannel =  AudioFormat.CHANNEL_OUT_MONO
            val outAudio = AudioFormat.ENCODING_PCM_16BIT
            val bufferSize = AudioTrack.getMinBufferSize(sampleRate, outChannel, outAudio)
            Log.i(TAG, "sampleRate: $sampleRate, bufferSize: $bufferSize , outChannel:$outChannel")
             audioTrack = AudioTrack(
                AudioAttributes.Builder()
                    .setUsage(AudioAttributes.USAGE_MEDIA)
                    .setContentType(AudioAttributes.CONTENT_TYPE_SPEECH)
                    .build(),
                AudioFormat.Builder()
                    .setEncoding(outAudio)
                    .setChannelMask(outChannel)
                    .setSampleRate(sampleRate)
                    .build(),
                bufferSize,
                AudioTrack.MODE_STREAM,
                0)
            audioTrack?.play()

            codec = MediaCodec.createDecoderByType(MediaFormat.MIMETYPE_AUDIO_AAC).apply {
                val format = MediaFormat.createAudioFormat(MediaFormat.MIMETYPE_AUDIO_AAC, sampleRate, 1)
                format.setInteger(MediaFormat.KEY_AAC_PROFILE, MediaCodecInfo.CodecProfileLevel.AACObjectLC)
//                format.setInteger(MediaFormat.KEY_CHANNEL_COUNT, 1)
//                format.setInteger(MediaFormat.KEY_SAMPLE_RATE, sampleRate)

// 关键：提供 CSD-0，不要设 KEY_IS_ADTS
//
                val csd0 = getAacDecoderConfigData(MediaCodecInfo.CodecProfileLevel.AACObjectLC, sampleRate, 1)
                format.setByteBuffer("csd-0", ByteBuffer.wrap(csd0))
//                val csd = byteArrayOf(0x12, 0x10) // AAC LC, 16kHz, mono
//                format.setByteBuffer("csd-0", ByteBuffer.wrap(csd))

                configure(format, null, null, 0)
                start()
            }
            running = true
            decodeThread = Thread {
                while (running) {
                    try {
//                        BLog.d(TAG, "audioDataBytesQueue size：${audioDataBytesQueue.size}")
                        val frame = audioDataBytesQueue.take()
//                        if(testFile == null){
//                            testFile = File(BaseApplication.instance().externalCacheDir,"test.aac")
//                            BLog.d("testFile", "create testFile :${testFile!!.absolutePath}")
//                            ///storage/emulated/0/Android/data/com.boby.homecamera/cache/test.aac
//                        }
//                        testFile?.appendBytes(frame)
                        decode(frame)
                    }catch (e: Exception){

                    }

                }
            }
            decodeThread?.start()
            pcmThread = Thread {
                while (running) {
                    try {
                        val frame = pcmDataBytesQueue.take()
//                         if(testFile == null){
//                            testFile = File(BaseApplication.instance().externalCacheDir,"test.pcm")
//                            BLog.d("testFile", "create testFile :${testFile!!.absolutePath}")
//                            ///storage/emulated/0/Android/data/com.boby.homecamera/cache/test.aac
//                        }
//                        testFile?.appendBytes(frame)
                        audioTrack?.write(frame, 0, frame.size)
                    }catch (e: Exception){

                    }

                }
            }.apply {
                start()
            }
        }

        fun onAudioFrame(bytes: ByteArray) {
            try {
                audioDataBytesQueue.offer( bytes)
            }catch (e: Exception){

            }

        }
        private fun decode(bytes: ByteArray) {
            if (!running) return
            val codec = codec ?: return
            val info = MediaCodec.BufferInfo()

            val inputIndex = codec.dequeueInputBuffer(10000)
            if (inputIndex >= 0) {
                val inputBuffer = codec.getInputBuffer(inputIndex)!!
                inputBuffer.clear()
                inputBuffer.put(bytes)
                codec.queueInputBuffer(inputIndex, 0, bytes.size, System.nanoTime() / 1000, 0)
            }

            var outputIndex = codec.dequeueOutputBuffer(info, 10000)
            BLog.d(TAG, "解码音频数据outputIndex：${outputIndex}")
            while (outputIndex >= 0) {
                val outBuffer = codec.getOutputBuffer(outputIndex)!!
                val pcm = ByteArray(info.size)
                outBuffer.get(pcm)
//                BLog.d(TAG, "解码音频数据pcm大小：${pcm.size}")
//                audioTrack?.write(pcm, 0, pcm.size)
                pcmDataBytesQueue.offer(pcm)
                codec.releaseOutputBuffer(outputIndex, false)
                outputIndex = codec.dequeueOutputBuffer(info, 0)
            }
        }
    private val bufferInfo = MediaCodec.BufferInfo()
        private fun decode2(bytes: ByteArray) {
            if (!running) return
            val decoder = codec ?: return

            val inIndex: Int = decoder.dequeueInputBuffer(10000L)
            if (inIndex >= 0) {
                // fill inputBuffers[inputBufferIndex] with valid data
                var byteBuffer: ByteBuffer?
                try {
                    byteBuffer = decoder.getInputBuffer(inIndex)
                } catch (e: Exception) {
                    e.printStackTrace()
                    return
                }
                byteBuffer?.rewind()

                // Preventing BufferOverflowException
//              if (length > byteBuffer.limit()) throw DecoderFatalException("Error")


                try {
//                    audioFrame = audioFrameQueue.pop()
//                    if (audioFrame == null) {
////                        Log.d(TAG, "Empty audio frame")
//                        // Release input buffer
//                        decoder.queueInputBuffer(inIndex, 0, 0, 0L, 0)
//                    } else {
                        byteBuffer?.put(bytes, 0, bytes.size)
                        decoder.queueInputBuffer(inIndex, 0, bytes.size, 0, 0)
//                    }
                } catch (e: Exception) {
                    e.printStackTrace()
                }
            }
//            Log.i(TAG, "inIndex: ${inIndex}")

            try {

                if (!running) return
                var outIndex = decoder.dequeueOutputBuffer(bufferInfo, 10000L)
                Log.w(TAG, "outIndex: ${outIndex}")
                while (outIndex >= 0){
                    val byteBuffer: ByteBuffer? = decoder.getOutputBuffer(outIndex)

                    val chunk = ByteArray(bufferInfo.size)
                    byteBuffer?.get(chunk)
                    byteBuffer?.clear()

                    if (chunk.isNotEmpty()) {
//                                audioTrack.write(chunk, 0, chunk.size)
                        try {
                            pcmDataBytesQueue.offer(chunk)
//                            audioBuffer.put(chunk)
                        }catch (e : Exception){
                            e.printStackTrace()
                        }

                    }
                    decoder.releaseOutputBuffer(outIndex, false)
                    outIndex = decoder.dequeueOutputBuffer(bufferInfo, 0)
                }
            } catch (e: Exception) {
                e.printStackTrace()
            }

            // All decoded frames have been rendered, we can stop playing now
            if (bufferInfo.flags and MediaCodec.BUFFER_FLAG_END_OF_STREAM != 0) {
                Log.d(TAG, "OutputBuffer BUFFER_FLAG_END_OF_STREAM")
                return
            }

        }

        fun stop() {
            running = false
            try {
                codec?.stop()
            }catch (e: Exception){

            }
            try {
                audioTrack?.stop()
                audioTrack?.release()
            }catch (e: Exception){

            }
            try {
                decodeThread?.interrupt()
            }catch (e: Exception){
                e.printStackTrace()
            }
            try {
                pcmThread?.interrupt()
            }catch (e: Exception){
                e.printStackTrace()
            }
            decodeThread = null
            pcmThread = null
        }


}